import {
  Component,
  ChangeDetectionStrategy,
  ViewEncapsulation,
  Input,
  OnInit,
  AfterViewInit,
  OnDestroy,
  TemplateRef,
  ViewChild,
  ViewContainerRef,
  ElementRef,
  Renderer2,
  ChangeDetectorRef,
  OnChanges,
  SimpleChanges,
  Output,
  EventEmitter,
  NgZone,
} from '@angular/core';
import { animate, AnimationEvent, group, state, style, transition, trigger } from '@angular/animations';
import { TemplatePortal } from '@angular/cdk/portal';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { FlexibleConnectedPositionStrategy, Overlay, OverlayConfig } from '@angular/cdk/overlay';
import { fromEvent, Subject, type Subscription } from 'rxjs';
import { debounceTime, takeUntil } from 'rxjs/operators';
import { OverlayService } from '../overlay/api';
import { TypeUtils } from '../utils/type';
import { InputBoolean, InputNumber } from '../utils/convert';
import { isEmpty } from '../utils/common';
import { MotionDuration, MotionFunc, MotionDurationTime } from '../animation/motion';
import { POSITION_MAP, ADJUST_POSITION_MAP, getPosition } from './tooltip-overlay-position';
import { PositionType, ThemeType, TriggerType } from './interface';

const ARROW_WIDTH = 16;
const ARROW_OFFSET = 16;

@Component({
  selector: 'lv-base-tooltip',
  templateUrl: './tooltip-base.component.html',
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  animations: [
    trigger('showTip', [
      state('topIn', style({ opacity: 1 })),
      state('topOut', style({ opacity: 0 })),
      state('bottomIn', style({ opacity: 1 })),
      state('bottomOut', style({ opacity: 0 })),
      state('rightIn', style({ opacity: 1 })),
      state('rightOut', style({ opacity: 0 })),
      state('leftIn', style({ opacity: 1 })),
      state('leftOut', style({ opacity: 0 })),
      transition('* => topIn', [
        style({ opacity: 0 }),
        group([
          animate(
            `${MotionDuration.SM} ${MotionFunc.EASE_IN_OUT}`,
            style({
              opacity: 1,
            }),
          ),
        ]),
      ]),
      transition('topIn => topOut', [
        group([
          animate(
            `${MotionDuration.XS} ${MotionFunc.EASE_IN_OUT}`,
            style({
              opacity: 0,
            }),
          ),
        ]),
      ]),
      transition('* => bottomIn', [
        style({ opacity: 0 }),
        group([
          animate(
            `${MotionDuration.SM} ${MotionFunc.EASE_IN_OUT}`,
            style({
              opacity: 1,
            }),
          ),
        ]),
      ]),
      transition('bottomIn => bottomOut', [
        group([
          animate(
            `${MotionDuration.XS} ${MotionFunc.EASE_IN_OUT}`,
            style({
              opacity: 0,
            }),
          ),
        ]),
      ]),
      transition('* => rightIn', [
        style({ opacity: 0 }),
        group([
          animate(
            `${MotionDuration.SM} ${MotionFunc.EASE_IN_OUT}`,
            style({
              opacity: 1,
            }),
          ),
        ]),
      ]),
      transition('rightIn => rightOut', [
        group([
          animate(
            `${MotionDuration.XS} ${MotionFunc.EASE_IN_OUT}`,
            style({
              opacity: 0,
            }),
          ),
        ]),
      ]),
      transition('* => leftIn', [
        style({ opacity: 0 }),
        group([
          animate(
            `${MotionDuration.SM} ${MotionFunc.EASE_IN_OUT}`,
            style({
              opacity: 1,
            }),
          ),
        ]),
      ]),
      transition('leftIn => leftOut', [
        group([
          animate(
            `${MotionDuration.XS} ${MotionFunc.EASE_IN_OUT}`,
            style({
              opacity: 0,
            }),
          ),
        ]),
      ]),
      transition('* => noneIn', []),
      transition('noneIn => noneOut', []),
    ]),
  ],
})
export class TooltipBaseComponent implements OnInit, AfterViewInit, OnDestroy, OnChanges {
  @Input() lvIcon: string; // 内容图标(tooltip属性)

  @Input() lvTheme: ThemeType; // 主题颜色

  @Input() lvClassName: string; // overlay样式类

  @Input() @InputNumber() lvZIndex: number; // 层级

  @Input() lvTrigger: TriggerType = 'hover'; // 触发方式

  @Input() lvPosition: PositionType = 'top'; // 展示位置，默认top，具体设置见POSITION_MAP

  @Input() lvOrigin: ElementRef = this.elementRef; // overlay触发源点

  @Input() @InputBoolean() lvBlockPageScroll = false; // 是否锁定页面滚动

  @Input() @InputBoolean() lvVisible: boolean = false; // 控制overlay是否显示,lvTrigger为customize时生效

  @Input() lvHeader: string | TemplateRef<void> = null; // 内容头部(popover属性)

  @Input() lvFooter: string | TemplateRef<void> = null; // 内容底部(popover属性)

  @Input() @InputBoolean() lvDisabled: boolean = false; // 是否禁用(popover属性)

  @Input() @InputBoolean() lvClosable: boolean = false; // 是否显示关闭按钮(popover属性)

  @Input() @InputBoolean() lvBackdrop: boolean = false; // 是否显示遮罩层(popover属性)

  @Input() @InputBoolean() lvShowArrow: boolean = true; // 控制overlay小三角是否显示

  @Input() lvContent: string | TemplateRef<void> = null; // 内容

  @Input() @InputBoolean() lvMaskTransparent: boolean = false; // 遮罩层是否透明(popover属性)

  @Input() @InputBoolean() lvArrowPointAtCenter: boolean = false; // 控制小三角是否指向锚点的中心

  @Input() lvAnimationType: 'popup' | 'slide' | 'none' = 'popup'; // overlay动画种类， none 为没有动效仅为内部使用

  @Input() lvScrollToClose: boolean | { exclusions: HTMLElement[] }; // 滚动时是否隐藏

  @Output() lvExternalTrigger: EventEmitter<any> = new EventEmitter(); // 外部触发关闭回调事件

  @Output() readonly lvVisibleChange: EventEmitter<boolean> = new EventEmitter();

  @ViewChild('tooltipTemplate', { static: true }) overlayTemplate: TemplateRef<any>; // 弹出层

  @Input() lvAfterOpen: () => (false | void | object) | Promise<false | void | object>; // overlay打开后自定义事件

  @Input() lvBeforeOpen: () => (false | void | object) | Promise<false | void | object>; // overlay打开前自定义事件

  @Input() lvAfterClose: () => (false | void | object) | Promise<false | void | object>; // overlay关闭后自定义事件

  @Input() lvOuterClosable: boolean | { exclusions: HTMLElement[]; includes?: string[] }; // 点击overlay外部时是否关闭

  @Input() lvBeforeClose: () => (false | void | object) | Promise<false | void | object>; // overlay关闭前自定义事件

  @Input() lvContextData: number | string | boolean | Array<any> | { [key: string]: any }; // 内容模板上下文数据
  _overlayRef; // overlay
  public element; // 组件DOM
  public animationState; // 动画状态
  public typeUtils = TypeUtils;
  public tipPosition: PositionType = 'top'; // position样式类名(小三角的展示位置)
  public openState$ = new Subject<boolean>(); // overlay开启状态
  public tooltipShow$ = new Subject<boolean>(); // tip是否显示

  public type: 'tooltip' | 'popover' = 'tooltip'; // 提示类型：信息提示|气泡卡片
  // slide模式下4个方向的动画map
  public animStateMap = {
    top: ['bottomIn', 'bottomOut'],
    bottom: ['topIn', 'topOut'],
    left: ['rightIn', 'rightOut'],
    right: ['leftIn', 'leftOut'],
  };

  private tipsKey: number;
  private offsetX: number = 0; // 箭头指向中心时，X轴的偏移量
  private offsetY: number = 0; // 箭头指向中心时，Y的偏移量
  private _isCallHide = false; //  hide的动画还未执行完就调用了show，会导致打开失败
  private prepareToShow = false;
  private mouseEvent: MouseEvent;
  private connectedPosition: any[]; // overlay 連結點位置参数
  private animationSub: Subscription;
  private destroy$ = new Subject<void>();
  private adjustAnimation$ = new Subject<void>();
  private triggerDisposables: Array<() => void> = [];
  private overlayDisposables: Array<() => void> = [];

  private positionStrategy: FlexibleConnectedPositionStrategy;

  constructor(
    public viewContainerRef: ViewContainerRef,
    public overlayService: OverlayService,
    public overlay: Overlay,
    public elementRef: ElementRef,
    public renderer: Renderer2,
    public cdr: ChangeDetectorRef,
    public ngZone: NgZone,
  ) {}

  /**
   * 关闭按钮方法(popover)
   */
  closeTooltip(): void {
    this._hide();
  }

  show(): void {
    this.tooltipShow$.next(true);
  }

  hide(): void {
    this.tooltipShow$.next(false);
  }

  /**
   * 内容是否为空,为空则不显示
   */
  isContentEmpty(): boolean {
    return this.lvContent instanceof TemplateRef ? false : isEmpty(this.lvContent);
  }

  updatePositionStrategy(): void {
    this._overlayRef?.overlayRef?.updatePositionStrategy(this.getPositionStrategy(this.positionStrategy));
  }

  positionChange(val): void {
    this.lvPosition = val;
    this.tipPosition = this.lvPosition;
    this.connectedPosition = this.getConnectPosition();
  }

  ngAfterViewInit(): void {
    // 设置组件触发DOM，和指令配合使用
    this.element = this.element ? this.element : this.elementRef.nativeElement;
    this.initializationTrigger();
    this.showObserv();
  }

  /**
   * lvVisible发生变化时，判断显示或隐藏tips。true：显示，false：隐藏
   * lvTrigger='customize'才生效，避免和默认的hover、click、focus触发方式显示隐藏功能冲突
   */
  visibleChange(val): void {
    this.lvVisible = coerceBooleanProperty(val);

    if (this.lvTrigger === 'customize') {
      if (this.lvVisible) {
        this.show();
      } else {
        this.hide();
      }
    }
  }

  ngOnDestroy(): void {
    if (this._overlayRef?.overlayRef.hasAttached()) {
      this._overlayRef.overlayRef.dispose();
    }
    this.destroy$.next();
    this.destroy$.complete();
    this.removeTriggerListeners();
    this.removeOverlayListeners();
  }

  getConnectPosition(): any[] {
    const position = [POSITION_MAP[this.lvPosition], ...ADJUST_POSITION_MAP];

    return position.map((item): { offsetX: number; offsetY: number } => ({
      ...item,
      offsetX: this.offsetX,
      offsetY: this.offsetY,
    }));
  }

  getAnimationPosMap(position): string {
    if (position.indexOf('top') > -1) {
      return 'top';
    } else if (position.indexOf('bottom') > -1) {
      return 'bottom';
    } else if (position.indexOf('left') > -1) {
      return 'left';
    } else if (position.indexOf('right') > -1) {
      return 'right';
    }
    throw new Error('internal error');
  }

  ngOnInit(): void {
    // 设置位置显示策略
    this.connectedPosition = this.getConnectPosition();
    this.ngZone.runOutsideAngular((): void => {
      fromEvent<MouseEvent>(document, 'mouseover')
        .pipe(takeUntil(this.destroy$))
        .subscribe((res): void => {
          this.mouseEvent = res;
        });
    });
  }

  setZIndex(overlayInstance: any): void {
    this.renderer.addClass(
      overlayInstance.overlayRef.hostElement,
      this.type === 'tooltip' ? 'lv-tooltip-z-index' : 'lv-popover-z-index',
    );

    if (this.lvZIndex) {
      this.renderer.setStyle(overlayInstance.overlayRef.hostElement, 'z-index', this.lvZIndex);
    }
  }

  getPositionStrategy(positionStrategy?: FlexibleConnectedPositionStrategy): FlexibleConnectedPositionStrategy {
    if (!positionStrategy) {
      return this.overlay.position().flexibleConnectedTo(this.lvOrigin).withPositions(this.connectedPosition);
    } else {
      return positionStrategy.setOrigin(this.lvOrigin).withPositions(this.connectedPosition);
    }
  }

  /**
   * 让组件更新其位置，用于鼠标移动时，改变tips位置
   */
  updatePosition(): void {
    if (this._overlayRef?.overlayRef.hasAttached()) {
      this._overlayRef.overlayRef.updatePosition();
      this.animationSub.unsubscribe();
      this.animationSub = this.adjustAnimation$.pipe(takeUntil(this.destroy$)).subscribe((): void => {
        this.setAnimationState('show');
        this.animationSub.unsubscribe();
      });
    }
  }

  ngOnChanges(changes: SimpleChanges): void {
    const { lvPosition, lvVisible, lvContent, lvOrigin } = changes;

    if (lvPosition) {
      this.positionChange(lvPosition.currentValue);
    }

    if (lvVisible) {
      this.visibleChange(lvVisible.currentValue);
    }

    if (lvContent) {
      if (this.isContentEmpty()) {
        this._hide();
      }
    }

    if (lvOrigin) {
      this.updatePositionStrategy();
    }
  }

  /**
   * 箭头小三角位置调整
   */
  adjustPosition(overlayInstance): void {
    // 每次打开的时候重新初始化期望的箭头位置
    this.tipPosition = this.lvPosition;
    // 利用debounceTime跳过初始化执行
    overlayInstance.overlayRef._positionStrategy.positionChanges.pipe(debounceTime(0)).subscribe((e): void => {
      this.tipPosition = getPosition(e.connectionPair) as PositionType;
      if (this.lvArrowPointAtCenter) {
        this.updateArrowPosition(e);
      }

      this.adjustAnimation$.next();
    });

    // 通知 overlay 刷新视图
    overlayInstance.overlayRef.updatePosition();
  }

  /**
   * 判断是否需要执行show、hide方法
   * 判断条件，在短时间订阅状态发生变化，则不去执行
   * 适用场景：离开target元素时，进入了overlay区域
   */
  showObserv(): void {
    this.tooltipShow$
      .pipe(
        debounceTime(MotionDurationTime.SM),
        // distinctUntilChanged(), // NEED_DO 只有当当前值与之前最后一个值不同时才将其发出
        takeUntil(this.destroy$),
      )
      .subscribe((show): void => {
        if (show) {
          this._show();
        } else {
          this.prepareToShow = false;
          this.closeByMouseTarget(
            this.mouseEvent,
            (): void => {
              this._hide();
            },
            false,
            this.tipsKey,
          );
        }
      });
  }

  /**
   * 设置动画状态
   * @param showState 状态参数
   */
  setAnimationState(showState: 'show' | 'hide'): void {
    if (showState === 'show') {
      const value = this.animStateMap[this.getAnimationPosMap(this.tipPosition)][0];
      this.animationState = this.lvAnimationType === 'none' ? 'noneIn' : value;
    } else if (showState === 'hide') {
      const value = this.animStateMap[this.getAnimationPosMap(this.tipPosition)][1];
      this.animationState = this.lvAnimationType === 'none' ? 'noneOut' : value;
    }

    this.cdr.markForCheck();
  }

  /**
   * hover显示tip，鼠标离开target元素
   * 进入overlay区域，overlay不消失
   * 离开overlay区域，overlay消失
   */
  overlayHover(overlayInstance): void {
    const setListen = (target): void => {
      this.overlayDisposables.push(
        this.renderer.listen(target, 'mouseenter', (): void => {
          this.tooltipShow$.next(true);
        }),
        this.renderer.listen(target, 'mouseleave', (): void => {
          this.tooltipShow$.next(false);
        }),
      );
    };

    if (this.lvTrigger === 'hover') {
      if (overlayInstance?.overlayRef.overlayElement) {
        // 弹框主体
        const target = overlayInstance.overlayRef.overlayElement.querySelector(`.lv-${this.type}-inner`);
        setListen(target);

        // 弹框小箭头
        const arrowTarget = overlayInstance.overlayRef.overlayElement.querySelector(`.lv-${this.type}-arrow`);
        if (arrowTarget) {
          setListen(arrowTarget);
        }
      }
    }
  }

  /**
   * 监听动画执行
   */
  onAnimationDone(event: AnimationEvent): void {
    const openState =
      (event.fromState === 'hide' && event.toState === 'show') ||
      (event.fromState === 'topOut' && event.toState === 'topIn') ||
      (event.fromState === 'bottomOut' && event.toState === 'bottomIn') ||
      (event.fromState === 'leftOut' && event.toState === 'leftIn') ||
      (event.fromState === 'rightOut' && event.toState === 'rightIn') ||
      (event.fromState === 'noneOut' && event.toState === 'noneIn');
    const closeState = ['hide', 'topOut', 'bottomOut', 'leftOut', 'rightOut', 'noneOut'].includes(event.toState);

    if (openState) {
      this.openState$.next(true);
    }
    if (closeState) {
      if (this.prepareToShow) {
        this.tooltipShow$.next(this.prepareToShow);
        this.prepareToShow = false;
      }
      this.openState$.next(false);
    }
  }

  /**
   * 注册触发器，判断执行显示隐藏条件
   */
  initializationTrigger(): void {
    if (this.lvTrigger === 'hover') {
      this.triggerDisposables.push(
        this.renderer.listen(this.element, 'mouseenter', (): void => {
          this.tooltipShow$.next(true);
        }),
        this.renderer.listen(this.element, 'mouseleave', (): void => {
          this.tooltipShow$.next(false);
        }),
      );
    } else if (this.lvTrigger === 'focus') {
      this.triggerDisposables.push(
        this.renderer.listen(this.element, 'focus', (): void => {
          this._show();
        }),
        this.renderer.listen(this.element, 'blur', (): void => {
          this._hide();
        }),
      );
    } else if (this.lvTrigger === 'click') {
      this.triggerDisposables.push(
        this.renderer.listen(this.element, 'click', (): void => {
          if (this._overlayRef?.overlayRef.hasAttached()) {
            this._hide();
          } else {
            this._show();
          }
        }),
      );
    }
  }

  updateArrowPosition(e): void {
    let el = this.element;
    if (this.lvOrigin !== this.elementRef) {
      el = this.lvOrigin?.nativeElement || this.lvOrigin;
    }

    const position = getPosition(e.connectionPair) as PositionType;
    const originRect = el.getBoundingClientRect();
    const offsetArrow = ARROW_OFFSET + ARROW_WIDTH / 2; // 偏移量等于箭头的位移加上宽度的一半
    let offsetX = 0;
    let offsetY = 0;

    if (position.endsWith('Left')) {
      offsetX = originRect.width / 2 - offsetArrow;
    } else if (position.endsWith('Right')) {
      offsetX = -(originRect.width / 2 - offsetArrow);
    } else if (position.endsWith('Top')) {
      offsetY = originRect.height / 2 - offsetArrow;
    } else if (position.endsWith('Bottom')) {
      offsetY = -(originRect.height / 2 - offsetArrow);
    }
    if (this.offsetX !== offsetX || this.offsetY !== offsetY) {
      this.offsetX = offsetX;
      this.offsetY = offsetY;
      this.connectedPosition = this.getConnectPosition();
      this._overlayRef.overlayRef.updatePositionStrategy(this.getPositionStrategy(this.positionStrategy));
      this.updatePosition();
    }
  }

  /**
   * 配置、打开overlay
   */
  openOverlay(): void {
    this.tipsKey = Number((Math.random() * 10).toFixed(0));
    this.positionStrategy = this.getPositionStrategy();
    const config = new OverlayConfig({
      hasBackdrop: this.lvBackdrop,
      backdropClass: [
        `lv-overlay-backdrop-${this.tipsKey}`,
        this.lvMaskTransparent ? 'cdk-overlay-transparent-backdrop' : 'cdk-overlay-dark-backdrop',
      ],
      positionStrategy: this.positionStrategy,
      scrollStrategy: this.lvBlockPageScroll
        ? this.overlay.scrollStrategies.block()
        : this.overlay.scrollStrategies.noop(),
    });
    const extendConfig = this.getExtendConfig();
    const _portal = new TemplatePortal(this.overlayTemplate, this.viewContainerRef);
    this.overlayService.open(_portal, config, extendConfig);
  }

  getExtendConfig(): any {
    const internalTriggerClose = (e): void => {
      const fn = (): void => {
        this._hide(); // 点击外部直接关闭，关闭后visible的值需跟随改变（此处visible为内部变更，不做显示隐藏功能）
        if (this.lvTrigger === 'customize') {
          this.lvVisible = false;
          this.lvVisibleChange.emit(this.lvVisible);
        }
        this.lvExternalTrigger.emit(e);
      };
      this.closeByMouseTarget(e, fn, true, this.tipsKey);
    };
    const beforeOpen = (): Promise<any> =>
      new Promise((resolve): void => {
        this.tooltipBeforeOpen(resolve);
      });
    const afterOpen = (overlayInstance): void => {
      this.setZIndex(overlayInstance);
      this._overlayRef = overlayInstance;
      this.tooltipAfterOpen(overlayInstance);
    };
    const beforeClose = (): Promise<any> =>
      new Promise((resolve): void => {
        this.removeOverlayListeners();
        this.tooltipBeforeClose(resolve);
      });
    const afterClose = (): void => {
      if (this.lvAfterClose) {
        this.lvAfterClose();
      }
    };
    return {
      backdropBindClose: this.lvOuterClosable !== false,
      clickToCloseStrategy: this.lvOuterClosable,
      scrollToCloseStrategy: this.lvScrollToClose,
      internalTriggerClose,
      beforeOpen,
      afterOpen,
      beforeClose,
      afterClose,
    };
  }

  /**
   * overlay Attached后执行操作
   */
  protected tooltipAfterOpen(overlayInstance: any): void {
    this.animationSub = this.adjustAnimation$.pipe(takeUntil(this.destroy$)).subscribe((): void => {
      this.setAnimationState('show');
      this.animationSub.unsubscribe();
    });
    this.adjustPosition(overlayInstance);
    this.overlayHover(overlayInstance);
    if (this.lvAfterOpen) {
      this.lvAfterOpen();
    }
  }

  /**
   * overlay Attached前执行操作
   */
  protected tooltipBeforeOpen(resolve): void {
    const openFn = (_resolve): void => {
      _resolve();
    };

    if (!this.lvBeforeOpen) {
      openFn(resolve);
    } else {
      const callBack: any = this.lvBeforeOpen();
      if (callBack instanceof Promise) {
        callBack.then((result): void => {
          if (result !== false) {
            openFn(resolve);
          }
        });
      } else if (callBack !== false) {
        openFn(resolve);
      }
    }
  }

  /**
   * overlay Detach前执行操作
   */
  protected tooltipBeforeClose(resolve): void {
    const closeFn = (_resolve): void => {
      /**
       * 执行完动画后再执行overlay的关闭操作
       */
      this.setAnimationState('hide');
      const closeSubscribe = this.openState$.subscribe((open): void => {
        if (!open) {
          closeSubscribe.unsubscribe();
          _resolve();
          if (this.lvTrigger === 'customize') {
            this.lvVisibleChange.emit(this.lvVisible);
          }
        }
      });
      this.cdr.markForCheck();
    };

    if (!this.lvBeforeClose) {
      closeFn(resolve);
    } else {
      const callBack: any = this.lvBeforeClose();
      if (callBack instanceof Promise) {
        callBack.then((result): void => {
          if (result !== false) {
            closeFn(resolve);
          }
        });
      } else if (callBack !== false) {
        closeFn(resolve);
      }
    }
  }

  private removeTriggerListeners(): void {
    this.triggerDisposables.forEach((item): void => item());
    this.triggerDisposables = [];
  }

  private removeOverlayListeners(): void {
    this.overlayDisposables.forEach((item): void => item());
    this.overlayDisposables = [];
  }

  /**
   * 隐藏tip
   */
  private _hide(): void {
    if (this._overlayRef?.overlayRef.hasAttached()) {
      this.setAnimationState('hide');
      this._overlayRef.close();
    }
    this._isCallHide = true;
  }

  private isExcludeOverlay(className, target): boolean {
    const overlayContainers = document.querySelectorAll(`.${className}`);
    const resultArray = Array.from(overlayContainers).map((container): boolean =>
      this.overlayService.getTargetNode(target, container),
    );
    return resultArray.some((item): boolean => item === true);
  }

  /**
   * 显示tip
   */
  private _show(): void {
    // 内容为空或者已经打开overlay则返回；是custom场景，增加_isCallHide作为判断之一。
    const _isCustomCallHide = this.lvTrigger === 'customize' ? this._isCallHide : false;
    const flag = !_isCustomCallHide && this._overlayRef?.overlayRef.hasAttached();
    if (this.isContentEmpty() || flag) {
      this.prepareToShow = true;
      return;
    }

    // 初始化 animation status
    this.setAnimationState('hide');
    this.openOverlay();
    this._isCallHide = false;
  }

  /**
   * 点击其他的overlay弹层时，不关闭popover（场景：在popover中，出现弹层，如select）
   * 当在Modal弹窗（或者class='lv-base-overlay-panel'的浮层）中打开popover，且lvOuterClosable为true时，点击modal的空白处要关闭
   */
  private closeByMouseTarget(e: MouseEvent, fn: () => void, isSourceTarget = true, tipsKey = 0): void {
    if (!e) {
      return;
    }
    const overlayContainer = document.querySelector('.cdk-overlay-container');
    const isModalContainer = this.isExcludeOverlay('lv-modal-panel', e.target);
    const isBaseOverlay = this.isExcludeOverlay('lv-base-overlay-panel', e.target);
    const isOverlay =
      this.overlayService.getTargetNode(e.target, overlayContainer) && !isModalContainer && !isBaseOverlay;
    const isBackdrop = (e.target as Element).classList?.toString().includes(`lv-overlay-backdrop-${tipsKey}`);
    let isObject = false;
    if (e.type === 'mousedown') {
      if (this.lvOuterClosable instanceof Object) {
        isObject = true;
      }
    } else if (this.lvScrollToClose instanceof Object) {
      isObject = true;
    }
    const flag = isBackdrop || isObject || !isOverlay || !isSourceTarget;
    if (flag) {
      fn();
    }
  }
}
